<?php
namespace hxhzyh\jwtAuth;

class JwtObject
{
    public const STATUS_OK = 1;//验证通过
    public const STATUS_SIGNATURE_ERROR = -1;//签名失败
    public const STATUS_EXPIRED = -2;//过期


    protected $alg = Jwt::ALG_METHOD_HMACSHA256; // 加密方式
    protected $iss = 'star'; // 发行人
    protected $exp; // 到期时间,不设置的话，永久有效，开发者可以通过redis等方式自定义单点登录，有效时间等
    protected $sub; // 主题
    protected $nbf; // 在此之前不可用
    protected $aud; // 用户
    protected $iat; // 发布时间
    protected $jti; // JWT ID用于标识该JWT
    protected $signature;
    protected $status = 0;
    protected $data;

    protected $secretKey;
    protected $header;
    protected $payload;

    public function __construct($data)
    {
        if(isset($data['alg'])) $this->alg = $data['alg'];
        if(isset($data['typ'])) $this->typ = $data['typ'];
        if(isset($data['exp'])) $this->exp = $data['exp'];
        if(isset($data['sub'])) $this->sub = $data['sub'];
        if(isset($data['nbf'])) $this->nbf = $data['nbf'];
        if(isset($data['aud'])) $this->aud = $data['aud'];
        if(isset($data['iat'])) $this->iat = $data['iat'];
        if(isset($data['jti'])) $this->jti = $data['jti'];
        if(isset($data['iss'])) $this->iss = $data['iss'];
        if(isset($data['status'])) $this->status = $data['status'];
        if(isset($data['data'])) $this->data = $data['data'];
        if(isset($data['signature'])) $this->signature = $data['signature'];
        if(isset($data['secretKey'])) $this->secretKey = $data['secretKey'];
        $this->initialize();
    }


    protected function initialize(): void
    {
        if(empty($this->nbf)){
            $this->nbf = time();
        }
        if(empty($this->iat)){
            $this->iat = time();
        }
        if(empty($this->jti)){
            $this->jti = Encryption::character(10);
        }

        // 解包：验证签名
        if(!empty($this->signature)){
            $signature = $this->signature();
            if($this->signature !== $signature){
                $this->status = self::STATUS_SIGNATURE_ERROR;
                return;
            }
            if($this->exp && time() > $this->exp){
                $this->status = self::STATUS_EXPIRED;
                return;
            }
        }
        $this->status = self::STATUS_OK;
    }

    /**
     * @return mixed
     */
    public function getAlg()
    {
        return $this->alg;
    }

    /**
     * @param mixed $alg
     * @return JwtObject
     */
    public function setAlg($alg): self
    {
        $this->alg = $alg;

        return $this;
    }

    /**
     * @return string
     */
    public function getIss(): string
    {
        return $this->iss;
    }

    /**
     * @param string $iss
     * @return JwtObject
     */
    public function setIss(string $iss): self
    {
        $this->iss = $iss;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getExp()
    {
        return $this->exp;
    }

    /**
     * @param mixed $exp
     * @return JwtObject
     */
    public function setExp($exp): self
    {
        $this->exp = $exp;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getSub()
    {
        return $this->sub;
    }

    /**
     * @param mixed $sub
     * @return JwtObject
     */
    public function setSub($sub): self
    {
        $this->sub = $sub;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getNbf()
    {
        return $this->nbf;
    }

    /**
     * @param mixed $nbf
     * @return JwtObject
     */
    public function setNbf($nbf): self
    {
        $this->nbf = $nbf;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getAud()
    {
        return json_decode(urldecode(Encryption::audDecryption(is_string($this->aud) ? $this->aud : json_encode($this->aud), $this->getSecretKey())), true);
    }

    /**
     * @param mixed $aud
     * @return JwtObject
     */
    public function setAud($aud): self
    {
        $this->aud = Encryption::audEncryption(urlencode(is_string($aud) ? $aud : json_encode($aud)), $this->getSecretKey());

        return $this;
    }

    /**
     * @return mixed
     */
    public function getIat()
    {
        return $this->iat;
    }

    /**
     * @param mixed $iat
     * @return JwtObject
     */
    public function setIat($iat): self
    {
        $this->iat = $iat;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getJti()
    {
        return $this->jti;
    }

    /**
     * @param mixed $jti
     * @return JwtObject
     */
    public function setJti($jti): self
    {
        $this->jti = $jti;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getSignature()
    {
        return $this->signature;
    }

    /**
     * @return int
     */
    public function getStatus(): int
    {
        return $this->status;
    }

    /**
     * @param mixed $data
     * @return JwtObject
     */
    public function setData($data): self
    {
        $this->data = $data;

        return $this;
    }

    public function getData()
    {
        return $this->data;
    }

    /**
     * @return mixed
     */
    public function getSecretKey()
    {
        return $this->secretKey;
    }

    /**
     * @param mixed $secretKey
     * @return JwtObject
     */
    public function setSecretKey($secretKey): self
    {
        $this->secretKey = $secretKey;
        return $this;
    }

    public function setHeader()
    {
        $algMap = [
            Jwt::ALG_METHOD_HMACSHA256 => Jwt::ALG_METHOD_HS256,
            Jwt::ALG_METHOD_AES => Jwt::ALG_METHOD_AES,
            Jwt::ALG_METHOD_HS256 => Jwt::ALG_METHOD_HS256
        ];

        $header = json_encode([
            'alg' => $algMap[$this->getAlg()],
            'typ' => 'JWT'
        ], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
        $this->header = Encryption::base64UrlEncode($header);
    }

    public function getHeader()
    {
        return $this->header;
    }

    public function setPayload()
    {
        $payload = json_encode([
            'exp' => $this->getExp(),
            'sub' => $this->getSub(),
            'nbf' => $this->getNbf(),
            'aud' => $this->aud,
            'iat' => $this->getIat(),
            'jti' => $this->getJti(),
            'iss' => $this->getIss(),
            'status' => $this->getStatus(),
            'data' => $this->getData()
        ], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);

        $this->payload = Encryption::base64UrlEncode($payload);
    }

    public function getPayload()
    {
        return $this->payload;
    }

    public function signature():?string
    {
        $this->setHeader();
        $this->setPayload();

        $content = $this->getHeader() . '.' . $this->getPayload();

        switch ($this->getAlg()){
            case Jwt::ALG_METHOD_HMACSHA256:
                $signature = Encryption::base64UrlEncode(
                    hash_hmac('sha256', $content, $this->getSecretKey(), true)
                );
                break;
            case Jwt::ALG_METHOD_HS256:
                $signature = Encryption::base64UrlEncode(
                    hash_hmac('sha256', $content, $this->getSecretKey(), true)
                );
                break;
            case Jwt::ALG_METHOD_AES:
                $signature = Encryption::base64UrlEncode(
                    openssl_encrypt($content, 'AES-128-ECB', $this->getSecretKey())
                );
                break;
            default : {
                $signature = '';
                break;
            }
        }

        return $signature;
    }

    public function getToken()
    {
        $this->signature = $this->signature();
        return $this->header . '.' . $this->payload . '.' . $this->signature;
    }



}